package com.dabai.iot.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dabai.common.core.constant.Constants;
import com.dabai.common.core.exception.ServiceException;
import com.dabai.common.core.utils.Func;
import com.dabai.common.core.utils.StreamUtils;
import com.dabai.common.core.utils.StringUtils;
import com.dabai.common.core.utils.TreeBuildUtils;
import com.dabai.iot.core.dto.DeviceGroupBind;
import com.dabai.iot.core.entity.DeviceGroupEntity;
import com.dabai.iot.core.entity.DeviceGroupRelationEntity;
import com.dabai.iot.core.mapper.DeviceGroupMapper;
import com.dabai.iot.core.service.IDeviceGroupRelationService;
import com.dabai.iot.core.service.IDeviceGroupService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 设备分组 Service 实现类
 *
 * @author kev1n
 */
@Service
@AllArgsConstructor
public class DeviceGroupServiceImpl extends ServiceImpl<DeviceGroupMapper, DeviceGroupEntity> implements IDeviceGroupService {

    final private IDeviceGroupRelationService deviceGroupRelationService;

    @Override
    public boolean addDeviceGroup(DeviceGroupEntity deviceGroup) {
        if (Func.isEmpty(deviceGroup.getParentId())) {
            deviceGroup.setParentId(Constants.TOP_PARENT_ID);
            deviceGroup.setAncestors(String.valueOf(Constants.TOP_PARENT_ID));
        }
        if (deviceGroup.getParentId() > 0) {
            DeviceGroupEntity parent = getById(deviceGroup.getParentId());
            String ancestors = parent.getAncestors() + StringUtils.SEPARATOR + deviceGroup.getParentId();
            deviceGroup.setAncestors(ancestors);
        }

        return save(deviceGroup);
    }

    @Override
    public boolean updateDeviceGroup(DeviceGroupEntity deviceGroup) {
        if (Func.isEmpty(deviceGroup.getParentId())) {
            deviceGroup.setParentId(Constants.TOP_PARENT_ID);
            deviceGroup.setAncestors(String.valueOf(Constants.TOP_PARENT_ID));
        }
        if (deviceGroup.getParentId() > 0) {
            if (Func.toLong(deviceGroup.getParentId()) == Func.toLong(deviceGroup.getId())) {
                // 父级分组不可选择自己
                throw new ServiceException("父级分组不可选择自己");
            }
            DeviceGroupEntity parent = getById(deviceGroup.getParentId());
            String ancestors = parent.getAncestors() + StringUtils.SEPARATOR + deviceGroup.getParentId();
            deviceGroup.setAncestors(ancestors);
        }

        return updateById(deviceGroup);
    }

    @Override
    public boolean removeDeviceGroup(Long id) {
        Long cnt = lambdaQuery().eq(DeviceGroupEntity::getParentId, id).count();
        if (cnt > 0) {
            // 存在子分组，无法删除
            throw new ServiceException("存在子分组，无法删除");
        }
        return removeById(id);
    }

    @Override
    public List<Tree<Long>> tree(String tenantId) {
        List<DeviceGroupEntity> groupList = lambdaQuery()
            .eq(Func.isNotEmpty(tenantId), DeviceGroupEntity::getTenantId, tenantId)
            .orderByAsc(DeviceGroupEntity::getSort)
            .list();

        return buildGroupTreeSelect(groupList);
    }

    public List<Tree<Long>> buildGroupTreeSelect(List<DeviceGroupEntity> groups) {
        if (CollUtil.isEmpty(groups)) {
            return CollUtil.newArrayList();
        }
        // 获取当前列表中每一个节点的 parentId，然后在列表中查找是否有 id 与其 parentId 对应，若无对应，则表明此时节点列表中，该节点在当前列表中属于顶级节点
        List<Tree<Long>> treeList = CollUtil.newArrayList();
        for (DeviceGroupEntity d : groups) {
            Long parentId = d.getParentId();
            DeviceGroupEntity groupEntity = groups.stream().filter(it -> it.getId().longValue() == parentId).findFirst().orElse(null);
            if (groupEntity == null) {
                List<Tree<Long>> trees = TreeBuildUtils.build(groups, parentId, (group, tree) ->
                    tree.setId(group.getId())
                        .setParentId(group.getParentId())
                        .setName(group.getGroupName())
                        .setWeight(group.getSort()));
                Tree<Long> tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getId());
                treeList.add(tree);
            }
        }
        return treeList;
    }

    @Override
    public boolean bindDevice(DeviceGroupBind entity) {
        Long deviceGroupId = entity.getDeviceGroupId();
        DeviceGroupEntity deviceGroup = getById(deviceGroupId);
        if (deviceGroup == null) {
            // 设备分组不存在
            throw new ServiceException("设备分组不存在");
        }

        List<DeviceGroupRelationEntity> relations = new ArrayList<>();

        List<Long> deviceIdList = Func.toLongList(entity.getDeviceIds());
        for (Long deviceId : deviceIdList) {
            Long count = deviceGroupRelationService.lambdaQuery()
                .eq(DeviceGroupRelationEntity::getGroupId, deviceGroupId)
                .eq(DeviceGroupRelationEntity::getDeviceId, deviceId)
                .count();
            if (count > 0) { // 已绑定过
                continue;
            }

            DeviceGroupRelationEntity relation = new DeviceGroupRelationEntity();
            relation.setGroupId(deviceGroupId);
            relation.setDeviceId(deviceId);
            relations.add(relation);
        }
        return deviceGroupRelationService.saveBatch(relations);
    }

    @Override
    public boolean unbindDevice(DeviceGroupBind entity) {
        Long deviceGroupId = entity.getDeviceGroupId();
        List<Long> deviceIdList = Func.toLongList(entity.getDeviceIds());

        LambdaQueryWrapper<DeviceGroupRelationEntity> wrapper = Wrappers.<DeviceGroupRelationEntity>query().lambda()
            .eq(DeviceGroupRelationEntity::getGroupId, deviceGroupId)
            .in(DeviceGroupRelationEntity::getDeviceId, deviceIdList);
        return deviceGroupRelationService.remove(wrapper);
    }

}
